package com.spring.component.exception;

import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

/**
 * @author fanfever
 * @email fanfeveryahoo@gmail.com
 * @url https://github.com/fanfever
 * @date 2016年7月9日
 */
@Slf4j
@ControllerAdvice
public class ControllerHandler {

    private static final String NOT_ALLOW_MESSAGE = "please stop your aggression, we have recorded your information.";

    private final MessageSource messageSource;

    @Autowired
    public ControllerHandler(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseEntity<BaseResponse<?>> processValidationError(MethodArgumentNotValidException ex) {
        ex.printStackTrace();
        BindingResult bindingResult = ex.getBindingResult();
        List<FieldError> fieldErrorList = bindingResult.getFieldErrors();
        BadRequestException badRequestException = processFieldError(fieldErrorList);
        return new ResponseEntity<>(new BaseResponse<>(badRequestException), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(IOException.class)
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public ResponseEntity<String> exceptionHandler(IOException e, HttpServletRequest request) {
        e.printStackTrace();
        if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(e), "Broken pipe")) {
            return null;
        } else {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.SERVICE_UNAVAILABLE);
        }
    }

    // XXX: BUG?
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ResponseBody
    public ResponseEntity<String> httpRequestMethodNotSupportedError(HttpRequestMethodNotSupportedException ex) {
        ex.printStackTrace();
        log.warn("httpRequestMethodNotSupportedError", ex);
        return new ResponseEntity<>(NOT_ALLOW_MESSAGE, HttpStatus.METHOD_NOT_ALLOWED);
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ResponseEntity<?> internalServerError(Exception ex) {
        ex.printStackTrace();
        if (ex instanceof BadRequestException) {
            log.warn("internalServerError BadRequestException");
            BadRequestException exception = (BadRequestException)ex;
            return new ResponseEntity<>(new BaseResponse<>(convMessage(exception)), HttpStatus.BAD_REQUEST);
        }
        if (ex instanceof HttpRequestMethodNotSupportedException) {
            log.warn("internalServerError HttpRequestMethodNotSupportedException");
            return new ResponseEntity<>(NOT_ALLOW_MESSAGE, HttpStatus.METHOD_NOT_ALLOWED);
        }
        if(ex instanceof AccessDeniedException){
            log.warn("internalServerError AccessDeniedException");
            return new ResponseEntity<>(new BaseResponse<>(ex), HttpStatus.FORBIDDEN);
        }
        log.error("ExceptionHandler", ex);
        return new ResponseEntity<>(new BaseResponse<>(ex), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    private BadRequestException processFieldError(List<FieldError> fieldErrorList) {
        BadRequestException badRequestExcetion = null;
        if (CollectionUtils.isNotEmpty(fieldErrorList)) {
            Locale locale = LocaleContextHolder.getLocale();
            List<String> messageList = fieldErrorList.stream().map(i -> messageSource.getMessage(i.getDefaultMessage(), null, locale))
                    .collect(Collectors.toList());
            badRequestExcetion = new BadRequestException(BaseException.ExceptionType.ERROR, messageList);
        }
        return badRequestExcetion;
    }

    private BadRequestException convMessage(BadRequestException badRequestException) {
        I18nMessage i18nMessage = badRequestException.getI18nMessage();
        if (i18nMessage != null) {
            List<String> messageList = Lists.newArrayListWithCapacity(1);
            messageList.add(messageSource.getMessage(i18nMessage.getCode(), i18nMessage.getParamList().toArray(), LocaleContextHolder.getLocale()));
            badRequestException.setMessageList(messageList);
        }
        return badRequestException;
    }
}
